package JarJunit;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarFile;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.regex.Pattern;

import org.junit.Test;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.TestClass;

import Common.TraceWriter;


public class JarJunitTestsParser {
	
	static public int get_test_classes_from_jar(String jar_file_path, List<String> tests_classes) throws IOException
	{
		TraceWriter.trace("JarJunitTestsParser proccessing jar file: "+jar_file_path);
		int total_num_of_tests = 0;
		
		try
		{
			JarFile jar_file = new JarFile(jar_file_path);
			
			URL url = new File(jar_file_path).toURL();
			URLClassLoader class_loader = new URLClassLoader(new URL[]{url});
			
			//iterate over all classes and look for tests method
			
			for (Enumeration<JarEntry> element = jar_file.entries(); element.hasMoreElements();) 
			{
            	JarEntry current_element = element.nextElement();
                String element_name = current_element.getName();
                
                int num_of_test_methods_in_class = check_for_test_methods(class_loader, element_name);
                
                if (num_of_test_methods_in_class > 0)
                {
                	total_num_of_tests+=num_of_test_methods_in_class;
                	tests_classes.add(element_name);
                }
			}
		}
		catch (IOException e)
		{
			TraceWriter.trace("could not open jar file "+jar_file_path);
			e.printStackTrace();
			throw e;
		}
		TraceWriter.trace("JarJunitTestsParser "+jar_file_path+" done!");
		
		return total_num_of_tests;
	}
	
	static private int check_for_test_methods(URLClassLoader class_loader, String element_name)
	{
		if (!element_name.endsWith(".class"))
		{
			return 0;
		}

		//fix class name
		String class_name_with_suffix = element_name.replace('/', '.');
		String class_name = class_name_with_suffix.substring(0, class_name_with_suffix.length()-6);
	
		//load class
		try
		{
			int total_num_of_tests_in_class = 0;
			Class class_instance = class_loader.loadClass(class_name);
			List<Method> result = new ArrayList<Method>();
	    	for (Method method : class_instance.getDeclaredMethods()) 
	    	{
	    	    Annotation ann = method.getAnnotation(Test.class);
	    	    if (ann != null) {
	    	    	total_num_of_tests_in_class++;
	    	    }
	    	}
	    	TraceWriter.trace("found JUNIT class. name: "+class_name+". number of tests: "+total_num_of_tests_in_class);
	    	return total_num_of_tests_in_class;
		}
		catch (ClassNotFoundException e)
		{
			TraceWriter.trace("error loading class: "+class_name);
			e.printStackTrace();
		}
		
		return 0;
	}
	
	
	static public List<String> get_test_methods_from_jar(String jar_file_path, String in_class_name) throws IOException
	{
		TraceWriter.trace("JarJunitTestsParser proccessing jar file: "+jar_file_path);
		
		List<String> list_of_methods = new ArrayList<String>();
		try
		{
			JarFile jar_file = new JarFile(jar_file_path);
			
			URL url = new File(jar_file_path).toURL();
			URLClassLoader class_loader = new URLClassLoader(new URL[]{url});
			
			//iterate over all classes and look for tests method
			
			for (Enumeration<JarEntry> element = jar_file.entries(); element.hasMoreElements();) 
			{
            	JarEntry current_element = element.nextElement();
                String element_name = current_element.getName();
                
                if (in_class_name.equals(element_name))
                {
                	get_test_methods_from_class(class_loader, element_name, list_of_methods);
                }
			}
		}
		catch (IOException e)
		{
			TraceWriter.trace("could not open jar file "+jar_file_path);
			e.printStackTrace();
			throw e;
		}
		TraceWriter.trace("JarJunitTestsParser "+jar_file_path+" done!");
		return list_of_methods;
	}
	
	static public void get_test_methods_from_class(URLClassLoader class_loader, String element_name,List<String> methods_names_list)
	{
		
		if (!element_name.endsWith(".class"))
		{
			return;
		}

		//fix class name
		String class_name_with_suffix = element_name.replace('/', '.');
		String class_name = class_name_with_suffix.substring(0, class_name_with_suffix.length()-6);
	
		//load class
		try
		{
			Class class_instance = class_loader.loadClass(class_name);
			List<Method> result = new ArrayList<Method>();
	    	for (Method method : class_instance.getDeclaredMethods()) 
	    	{
	    	    Annotation ann = method.getAnnotation(Test.class);
	    	    if (ann != null) {
	    	    	methods_names_list.add(method.getName());
	    	    }
	    	}
		}
		catch (ClassNotFoundException e)
		{
			TraceWriter.trace("error loading class: "+class_name);
			e.printStackTrace();
		}
		
		return;
	}
	
	public static void categorize_methods(List<String> all_methods, JarJunitResult testResult, List<String> passed_methods, List<String> failed_methods)
	{
		for (FailedTestResult failed_result: testResult.getFailedTestsList())
		{
			String test_name = failed_result.getTestName();
			String[] splits = test_name.split(Pattern.quote("("));
			String failed_result_method_name = splits[0];
			failed_methods.add(failed_result_method_name);
		}
		
		for (String method_name: all_methods)
		{
			boolean is_passed_method = true;
			for (String failed_method: failed_methods)
			{
				if (method_name.equals(failed_method))
				{
					is_passed_method = false;
					break;
				}
			}
			if (is_passed_method)
				passed_methods.add(method_name);
		}
	}
}
